package com.ds.infra.rpc.autoconfigure.scanner;

import com.ds.infra.rpc.annotation.RpcService;

/**
 * 作用：RPC服务扫描器实现
 *
 * @author WeiShaoying
 */
import com.ds.infra.rpc.core.cache.ServiceCache;
import com.ds.infra.rpc.properties.RpcProperties;
import com.ds.infra.rpc.registry.service.ServiceRegistry;
import com.ds.infra.rpc.registry.util.ServiceKeyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.net.InetAddress;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 服务提供者扫描器
 */
@Component
@Slf4j
public class RpcServiceScanner implements BeanPostProcessor {

    @Autowired
    private ServiceRegistry serviceRegistry;

    @Autowired
    private RpcProperties properties;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        RpcService rpcService = AnnotationUtils.findAnnotation(targetClass, RpcService.class);

        if (rpcService != null) {
            registerService(bean, targetClass, rpcService);
        }
        return bean;
    }

    // @Override
    public Object postProcessAfterInitialization2(Object bean, String beanName) {
        RpcService annotation = AnnotationUtils.findAnnotation(bean.getClass(), RpcService.class);
        if (annotation != null) {
            Class<?> interfaceClass = annotation.serviceInterface() != void.class
                    ? annotation.serviceInterface()
                    : getPrimaryInterface(bean.getClass());

            String serviceKey = ServiceKeyUtils.buildServiceKey(
                    interfaceClass.getName(),
                    annotation.version()
            );

            ServiceCache.addService(serviceKey, bean);
            log.info("====Registered service: {}", serviceKey);
        }
        return bean;
    }

    private Class<?> getPrimaryInterface(Class<?> clazz) {
        Class<?>[] interfaces = clazz.getInterfaces();
        if (interfaces.length != 1) {
            throw new IllegalArgumentException("Service must implement exactly one interface");
        }
        return interfaces[0];
    }

    private void registerService(Object bean, Class<?> targetClass, RpcService rpcService) {
        try {
            Class<?> interfaceClass = resolveServiceInterface(targetClass, rpcService);
            String serviceName = interfaceClass.getName();
            String serviceKey = ServiceKeyUtils.buildServiceKey(serviceName, rpcService.version());

            // 获取本机IP和配置的端口
            String host = InetAddress.getLocalHost().getHostAddress();
            // int port = applicationContext.getEnvironment()  .getProperty("rpc.serverPort", Integer.class, 8080);
            int port = properties.getServerPort();
            // 注册服务元数据
            Map<String, Object> serviceMeta = new LinkedHashMap<>();
            serviceMeta.put("serviceName", serviceName);
            serviceMeta.put("serviceVersion", rpcService.version());
            serviceMeta.put("serviceAddress", host + ":" + port);
            serviceMeta.put("serviceClass", targetClass.getName());

            serviceRegistry.register(serviceKey, serviceMeta);
            log.info("==== Registered service: {} => {}", serviceKey, serviceMeta);

            ServiceCache.addService(serviceKey, bean); // 缓存服务实例
            log.info("==== Put ServiceCache {}", serviceKey);
        } catch (Exception e) {
            throw new IllegalStateException("Failed to register RPC service: " + targetClass.getName(), e);
        }
    }

    private Class<?> resolveServiceInterface(Class<?> targetClass, RpcService rpcService) {
        Class<?> interfaceClass = rpcService.serviceInterface();
        if (void.class.equals(interfaceClass)) {
            Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(targetClass);
            if (interfaces.length == 1) {
                interfaceClass = interfaces[0];
            } else {
                throw new IllegalArgumentException("Please specify serviceInterface in @RpcService for " + targetClass.getName());
            }
        }
        return interfaceClass;
    }
}
